package fractal.filter;

import fractal.ColorMap;
import jinyilw.common.ThreadTools;

import java.awt.image.DataBuffer;

public abstract class FractalFilter implements Cloneable
{
	public static final FractalFilter[] filterArr = {new ContrastFilter(),
			new HSLFilter(), new EdgeFilter(), new SolarizeFilter(),
			new EqualizeFilter(), new SpotlightFilter(),
			new CrystallizeFilter(), new PointillizeFilter(), new NoneFilter()};

	protected int[] inPixels, outPixels;
	protected int total;
	protected int width, height;
	protected DataBuffer dataBuffer;
	protected int backColorPix;
	protected ColorMap colorMap;
	protected int[] nDatas;

	protected int core;

	public FractalFilter()
	{
	}

	@Override
	public FractalFilter clone()
	{
		try
		{
			return (FractalFilter) super.clone();
		} catch (CloneNotSupportedException e)
		{
			e.printStackTrace();
			return null;
		}
	}

	private Thread[] createThreads(int core, int total)
	{
		Thread[] threads = new Thread[core];
		int d = total / core, l = core - 1;
		int steps = 0;

		for (int i = 0; i < l; i++)
		{
			threads[i] = new MThread(steps, steps + d);
			steps += d;
		}
		threads[l] = new MThread(steps, total);
		return threads;
	}

	private class MThread extends Thread
	{
		int start, end;

		public MThread(int start, int end)
		{
			this.start = start;
			this.end = end;
		}

		@Override
		public void run()
		{
			calculateData(start, end);
		}
	}

	protected abstract void initFilter();

	protected abstract int calculatePixel(int i);

	public void filter(int[] inPixels, int width, int height,
			DataBuffer dataBuffer, int backColorPix, ColorMap colorMap,
			int[] nDatas, int core)
	{
		filter(inPixels, null, width, height, dataBuffer, backColorPix,
				colorMap, nDatas, core);
	}

	public void filter(int[] inPixels, int[] outPixels, int width, int height,
			int backColorPix, ColorMap colorMap, int[] nDatas, int core)
	{
		filter(inPixels, outPixels, width, height, null, backColorPix, colorMap,
				nDatas, core);
	}

	public synchronized void filter(int[] inPixels, int[] outPixels, int width,
			int height, DataBuffer dataBuffer, int backColorPix,
			ColorMap colorMap, int[] nDatas, int core)
	{
		if (inPixels == null)
			return;
		this.inPixels = inPixels;
		this.outPixels = outPixels;
		this.width = width;
		this.height = height;
		total = width * height;
		this.dataBuffer = dataBuffer;
		this.backColorPix = backColorPix;
		this.colorMap = colorMap;
		this.nDatas = nDatas;
		this.core = core;
		initFilter();
		if (core > 1)
		{
			ThreadTools.startAndJoin(createThreads(core, total));
		} else
			calculateData(0, total);
	}

	protected void calculateData(int min, int max)
	{
		if (dataBuffer != null)
		{
			if (colorMap != null)
				scan:for (int i = min; i < max; i++)
				{
					if (inPixels[i] == 0)
					{
						dataBuffer.setElem(i, backColorPix);
						continue;
					} else
					{
						for (int j = 0; j < colorMap.length; j++)
							if (nDatas[i] == colorMap.getKey(j))
							{
								dataBuffer.setElem(i, colorMap.getValue(j));
								continue scan;
							}
					}
					dataBuffer.setElem(i, calculatePixel(i));
				}
			else
				for (int i = min; i < max; i++)
				{
					if (inPixels[i] == 0)
					{
						dataBuffer.setElem(i, backColorPix);
						continue;
					}
					dataBuffer.setElem(i, calculatePixel(i));
				}
		} else if (outPixels != null)
		{
			if (colorMap != null)
				scan:for (int i = min; i < max; i++)
				{
					if (inPixels[i] == 0)
					{
						outPixels[i] = backColorPix;
						continue;
					} else
					{
						for (int j = 0; j < colorMap.length; j++)
							if (nDatas[i] == colorMap.getKey(j))
							{
								outPixels[i] = colorMap.getValue(j);
								continue scan;
							}
					}
					outPixels[i] = calculatePixel(i);
				}
			else
				for (int i = min; i < max; i++)
				{
					if (inPixels[i] == 0)
					{
						outPixels[i] = backColorPix;
						continue;
					}
					outPixels[i] = calculatePixel(i);
				}
		}
	}

	public int clamp(int value)
	{
		return value > 255 ? 255 : (value < 0 ? 0 : value);
	}
}
